Fresh 入門
はじめに
Freshに関する概要などについては、Freshを参照ください🙇♂️ プロジェクトの初期化
init.tsを実行すると、プロジェクトが作成されます
code:shell
deno.jsonやroutes/ディレクトリなど、開発に必要なファイルが自動生成されます。 devサーバの起動
開発をする際は、devサーバを起動する必要があります。以下のコマンドによって起動することが可能です。(Freshでは開発に必要な各種コマンドはdeno task経由で提供されます) code:shell
$ deno task start
ルーティング
routesディレクトリ
Routeを配置するディレクトリです。
routesディレクトリ内の構造を元に、最終的なパスが決定されます。
Dynamic routes
routes/users/[id].tsxのような形式でファイルを用意すると定義できます。
Route Groups
Route Groupsを定義する際は、routes/配下に(some-group)というような形式のディレクトリを用意します。
例)
code:text
routes
├── (_islands)
│ └── Counter.tsx
├── (dashboard) # Route Groupの定義
│ ├── _layout.tsx # Route GroupごとにLayoutを適用できます
│ ├── _middleware.ts # Route GroupごとにMiddlewareを適用できます
│ └── account.tsx # /accountにマッピングされます ((dashboard)の部分はパスから取り除かれます)
├── _app.tsx
├── _layout.tsx
└── index.tsx
例外として、(_some-group)というように_から始まるグループについてはFreshによってファイルシステムルーティングの対象から除外されます。 この性質を利用することで、例えば、(_components)のようなディレクトリを用意し、そこで特定のページに関連するコンポーネントの一覧をまとめて管理することもできます。
さらに例外として、(_islands) というディレクトリについてはFreshによって特別扱いされます。具体的には、このディレクトリ配下の各モジュールをFreshはIslandコンポーネントとして取り扱います。 Route
Routeとはページまたはハンドラのいずれかまたはその両方を定義したモジュールのことです。
ページ
RouteからPreactコンポーネントをdefault exportすると、対応するパスにアクセスした際にそのコンポーネントがSSRされます。 ハンドラ
後述するカスタムハンドラを定義することで、ページのレンダリング時の挙動のカスタマイズなどが可能です。
あるRouteでカスタムハンドラを定義していない場合、同一Routeでdefault exportしているページコンポーネントをSSRするハンドラがデフォルトで実行されます。 カスタムハンドラ
APIエンドポイントの作成やなど様々な用途で活用できます。
カスタムハンドラを定義したいときは、Routeから下記いずれかの形式でhandlerをexportする必要があります。
(request: Request, ctx: HandlerContext) => Response | Promise<Response>
{ [httpMethod: string]: (request: Request, ctx: HandlerContext) => Response | Promise<Response> }
カスタムハンドラでctx.render()を呼ぶと、同一Routeで定義されているページコンポーネントがレンダリングされます。
また、ctx.render()に渡した引数は、対応するページコンポーネントのprops.data経由でアクセスできます。
code:typescript
import type { Handlers, PageProps } from "$fresh/server.ts";
export const handler: Handlers<Data> = {
async GET(req, ctx) {
const user = await findUserByID(ctx.params.id);
if (user == null) {
return ctx.renderNotFound();
}
const resp = await ctx.render(user);
return resp;
},
};
export default async function User(props: PageProps<User>) {
return <UserDetail user={props.data} />;
}
Async route component
Freshが提供する独自形式のコンポーネントです。 RequestオブジェクトとRouteContextを受け取り、Vnodeを返却する関数です。
code:typescript
import { defineRoute } from "$fresh/server.ts";
export default defineRoute(async (req, ctx) => {
const user = await findUserByID(ctx.params.id);
if (user == null) {
return ctx.renderNotFound();
}
return <UserDetail user={user} />;
});
Islands
概要
Hydrationが必要な場合は、Islandコンポーネントを実装する必要があります。
Freshではislandsディレクトリまたはroutes/**/(_islands)ディレクトリに配置したコンポーネントは、Islandとして扱われます。 命名形式
Islandコンポーネントのファイル名はパスカルケースまたはケバブケース形式で命名する必要があります。
ミドルウェア
ミドルウェアを定義することで、ハンドラの実行前後に任意の処理を挟めます。
routesディレクトリ配下に_middleware.tsを配置し、handler関数をexportすることで定義できます。
handlerは引数としてRequestとMiddlewareHandlerContextを受け取り、MiddlewareHandlerContextのnextを呼ぶことで、後続のハンドラが実行されます。
エラーページ
404エラー
routes/_404.tsxでコンポーネントをdefault exportすることで、404エラー用のページをカスタマイズできます (コンポーネントにはUnknownPagePropsが渡されます)
404ページを明示的にレンダリングしたいときは、HandlerContext#renderNotFoundを利用できます。
code:with-render-not-found.tsx
export const handler: Handlers = {
GET(req, ctx) {
return ctx.renderNotFound();
},
};
500エラー
routes/_500.tsxでコンポーネントをdefault exportすることで、500エラー用のページをカスタマイズできます (コンポーネントにはErrorPagePropsが渡されます)
Custom App
routes/_app.tsxに配置します。
Custom Appコンポーネントに渡されるpropsについてはAppProps型を参照 Layout
Custom App(routes/_app.tsx)はアプリケーションにつき一つしか配置できない制限があります。
それに対して、Layoutはroutes内の任意のディレクトリに_layout.tsxを配置することで定義できます。
code:typescript
// routes/admin/_layout.tsx
import type { LayoutProps } from "$fresh/server.ts";
export default function AdminLayout({ params, Component }: LayoutProps) {
return (
<section>
<h2>Admin</h2>
<div>
<Component />
</div>
</section>
);
}
Freshで提供されるコンポーネント
<Head> - <head>タグの内容を組み立てる際に使用できます。 (将来的に非推奨化される予定です)
静的ファイル
staticディレクトリに配置したファイルは静的に配信されます
Freshが提供するasset関数を利用することで、staticディレクトリ内のファイルへの絶対パスをビルドID付きで取得することもできます。 また、Freshは<img>や<source>のsrcやsrcset属性のパスに対して、自動でビルドIDを付与してくれます。(この挙動を無効化したいときは、data-fresh-disable-lockを指定する必要があります) 事前ビルド (AOTコンパイル)
Freshではdeno task buildコマンドによって、Islandコンポーネントなどを事前にビルドすることができます。 code:shell
$ deno task build
このコマンドを実行すると、esbuildによるバンドル結果が_freshディレクトリに出力されます。 Freshはもし_freshディレクトリが存在すれば、そこに格納されたバンドルを利用します。 これによりコールドスタート時間の短縮が期待されます。
注意点として、Freshにおいては事前ビルドはオプトインであり、導入は必須ではありません。 例えば、開発時は事前ビルドは使わずにJITレンダリング方式のみを使用して開発を進め、本番にデプロイするときだけ事前ビルド(AOTコンパイル)を行う、といったことも可能です。
プラグイン
公式ではTwind v0.16向けのプラグインが提供されています。 テスト
アップデート
code:shell